﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Documents;
using System.ComponentModel.Composition;

using Microsoft.Practices.Prism.ViewModel;

using QLab.Model;
using QLab.UserControls;
using QLab.Services;
using Microsoft.Practices.Prism.Events;

namespace QLab.Controllers
{
	[Export(typeof(ConsoleController))]
	[PartCreationPolicy(CreationPolicy.Shared)]
	public class ConsoleController : NotificationObject
	{
		private ICodeExecutionService _codeExecutionService;
		private ICommandHistoryService _commandHistoryService;
		private IEventAggregator _eventAggregator;

		private ConsoleView _view;

		private CommandIterator _commandIterator;
		private string _prompt = ">> ";

		public delegate void CommandExecutingEventHandler(object sender, CommandEventArgs e);
		public event CommandExecutingEventHandler CommandExecuting;

		[ImportingConstructor]
		public ConsoleController(ICodeExecutionService CodeExecutionService, ICommandHistoryService CommandHistoryService, IEventAggregator EventAggregator)
		{
			_codeExecutionService = CodeExecutionService;
			_commandHistoryService = CommandHistoryService;

			_commandIterator = _commandHistoryService.CreateIterator();

			_eventAggregator = EventAggregator;
		}

		#region Properties

		public ConsoleView View
		{
			get { return _view; }
			set
			{
				if (value != null && value != _view)
				{
					_view = value;
					_view.CurrentCommand.PreviewKeyDown += new KeyEventHandler(CurrentCommand_PreviewKeyDown);
					_view.CurrentCommand.PreviewTextInput += new TextCompositionEventHandler(CurrentCommand_PreviewTextInput);
					_view.ConsoleText.PreviewTextInput += new TextCompositionEventHandler(ConsoleText_PreviewTextInput);
					_view.CurrentCommand.AppendText(_prompt);

					RaisePropertyChanged(() => View);
				}
			}
		}

		#endregion

		public void ReplaceCommand(string command)
		{
			_view.CurrentCommand.Text = _prompt + command;
			_view.CurrentCommand.CaretIndex = _view.CurrentCommand.Text.Length;
		}

		void CurrentCommand_PreviewTextInput(object sender, TextCompositionEventArgs e)
		{
			if (_view.CurrentCommand.CaretIndex < _prompt.Length)
			{
				e.Handled = true;
				_view.CurrentCommand.AppendText(e.Text);
				_view.CurrentCommand.CaretIndex = _view.CurrentCommand.Text.Length;
			}
		}

		void ConsoleText_PreviewTextInput(object sender, TextCompositionEventArgs e)
		{

			e.Handled = true;
			_view.ConsoleContainer.ScrollToBottom();
			_view.CurrentCommand.AppendText(e.Text);
			_view.CurrentCommand.CaretIndex = _view.CurrentCommand.Text.Length;
		}

		void CurrentCommand_PreviewKeyDown(object sender, KeyEventArgs e)
		{
			switch (e.Key)
			{
				case Key.Enter:
					OnEnterPressed(e);
					break;
				case Key.Up:
					OnUpPressed(e);
					break;
				case Key.Down:
					OnDownPressed(e);
					break;
				case Key.Back:
				case Key.Delete:
					OnBackPressed(e);
					break;
			}
		}

		private void OnEnterPressed(KeyEventArgs e)
		{
			string tempCommand = _view.CurrentCommand.Text.Substring(_prompt.Length).Trim();
			_codeExecutionService.ExecuteCommand(tempCommand);
			if (String.Compare(Text, "\r\n") == 0)
			{
				_view.ConsoleText.Height = Double.NaN;
				_view.ConsoleText.AppendText(_prompt + tempCommand);
			}
			else
				_view.ConsoleText.AppendText('\r' + _prompt + tempCommand);
			_view.CurrentCommand.Text = _prompt;
			_view.CurrentCommand.CaretIndex = _view.CurrentCommand.Text.Length;
			_view.ConsoleContainer.ScrollToBottom();
			e.Handled = true;
		}

		private void OnUpPressed(KeyEventArgs e)
		{
			ReplaceCommand(_commandIterator.Previous() ?? "");
			if (_commandIterator.IsAtBegin())
			{
				System.Media.SystemSounds.Beep.Play();
				_commandIterator.ToLastCommand();
			}
			e.Handled = true;
		}

		private void OnDownPressed(KeyEventArgs e)
		{
			ReplaceCommand(_commandIterator.Next() ?? "");
			if (_commandIterator.IsAtEnd())
				System.Media.SystemSounds.Beep.Play();
			e.Handled = true;
		}

		private void OnBackPressed(KeyEventArgs e)
		{
			if (_view.CurrentCommand.CaretIndex <= _prompt.Length)
			{
				e.Handled = true;
				System.Media.SystemSounds.Beep.Play();
			}
		}

		private string Text
		{
			get
			{
				TextRange textRange = new TextRange(_view.ConsoleText.Document.ContentStart, _view.ConsoleText.Document.ContentEnd);
				return textRange.Text;
			}
			set
			{
				_view.ConsoleText.Document.Blocks.Clear();
				_view.ConsoleText.Document.ContentStart.InsertTextInRun(value.Trim());
				_view.ConsoleText.CaretPosition = _view.ConsoleText.Document.ContentEnd;
			}
		}

		public void OnCommandExecuting(CommandEventArgs e)
		{
			if (CommandExecuting != null)
				CommandExecuting(this, e);
		}
	}
}